package tms.unitTests;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;

import java.util.Calendar;
import java.util.Date;

import org.junit.Before;
import org.junit.Test;

import tms.model.Person;
import tms.model.Task;

/**
 * PersonUnitTests class contains the units tests of the methods in the Person class 
 * 
 * @author Amer Alhalabi/Rolf Schmidt
 *
 */
public class PersonUnitTests {

	/// a header line used in converting a person or a task to a string
	final String H_LINE = "---------------------------------------";
	/// format of the date
	final String DATE_FORMAT = "MM/dd/yyyy";
	/// number of characters in a long string
	final int LONG_STRING = 100;
	/// number of characters in a very long string
	final int VERY_LONG_STRING = 2000;
	/// a string containing wild characters
	final String WILD_STRING = "*@&(|}{+?~+\\/";
	/// stringbuffer for long string
	StringBuffer stringBufferLong;
	/// stringbuffer for a very long string
	StringBuffer stringBufferVeryLong;
		
	/**
	 * default constructor
	 */
	public PersonUnitTests() {
    }
	
	/**
	 * helper method called before every unit test to create a long and a very long string buffers
	 */
	@Before
	public void setUp() {
		stringBufferLong = new StringBuffer(LONG_STRING);
		for (int i = 0; i < LONG_STRING; i++) {
			stringBufferLong.append("a");
		}
		
		stringBufferVeryLong = new StringBuffer(VERY_LONG_STRING);
		for (int i = 0; i < VERY_LONG_STRING; i++) {
			stringBufferVeryLong.append("a");
		}
    }
	
	/**
	 * test the default values assigned to a person object in the person default constructor
	 */
	@Test
	public void testPersonConstructor() {
		// create a person using the default constructor
        Person person = new Person();
        
        // test the default values
        assertEquals("nobody", person.getName());
        assertEquals(0, person.getHours());
        assertTrue(person.getTasks().isEmpty());
    }
	
	/**
	 * test converting a newly created person to a string
	 * the default constructor in person class will be used to create the person object
	 */
	@Test
	public void testPersonToString1() {
		
		// create a new person object
		Person person = new Person();
		
		// create a string
		StringBuffer sb = new StringBuffer(H_LINE);
		sb.append("\nName: nobody");
		sb.append("\nHours: 0"); 
		sb.append("\nTasks: "); 
		sb.append("\n" + H_LINE + "\n");
		
		// test
		assertEquals(sb.toString(), person.toString());
	}

	/**
	 * test converting a person to a string
	 * the person object is create using the default constructor
	 *  and then its name is modified
	 *  after that a task is assigned to that person
	 */
	@Test
	public void testPersonToString2() {
		// create a new person object
		Person person = new Person();
		// modify the person object
		person.setName("Jack Frankie");

		// create a string buffer to hold the expected value
		StringBuffer sb = new StringBuffer(H_LINE);
		sb.append("\nName: Jack Frankie");
		sb.append("\nHours: 0"); 
		sb.append("\nTasks: "); 
		sb.append("\n" + H_LINE + "\n");
		
		// test
		assertEquals(sb.toString(), person.toString());
		
		// clear string buffer
		sb.delete(0, sb.length());
		
		// modify person again by adding a task
		Task task = new Task();
		Date testDeadline = Calendar.getInstance().getTime();
		task.setDeadline(testDeadline);
		task.setId(1);
		task.setTitle("Test title");
		task.setDescription("Test description");
		task.setDuration(25);
		task.setDeliverable("Test deliverable");
		task.setPerson("Jack Frankie");
			
		person.addTask(task);
		
		// create the equivalent
		sb.append(H_LINE);
		sb.append("\nName: Jack Frankie");
		// 25 days * 8 working hours per day
		sb.append("\nHours: 200"); 
		sb.append("\nTasks: 1");
		sb.append("\n" + H_LINE + "\n");
		
		// test
		assertEquals(sb.toString(), person.toString());
	}
	
	/**
	 * test that a person object is equal to itself
	 */
	@Test
	public void testPersonEquals1() {
		Person person = new Person();
		
		assertTrue(person.equals(person));
	}
	

	/**
	 * test that two different person objects are equal when all of their attributes are the same
	 * the names are set in the test and the rest of attributes are defaults
	 */
	@Test
	public void testPersonEquals2() {
		
		Person person1 = new Person();
		Person person2 = new Person();
		
		person1.setName("Jakie hyman");
		person2.setName("Jakie hyman");
		
		assertNotSame(person1, person2);
		assertTrue(person1.equals(person2));
		assertTrue(person2.equals(person1));
	}
	
	/**
	 * test that two person objects are different if their names are different
	 * the difference is a white space
	 */
	@Test
	public void testPersonEquals3() {
		Person person1 = new Person();
		Person person2 = new Person();
		
		/// space difference
		person1.setName("Jakie hyman ");
		person2.setName("Jakie hyman");
		
		assertNotSame(person1, person2);
		assertFalse(person1.equals(person2));
		assertFalse(person2.equals(person1));
	}
	
	/**
	 * test that a person object is not equal to an object of different type
	 */
	@Test
	public void testPersonEquals4() {
		
		Person person = new Person();
		String test = "test different object";
		
		// test that person is not equal to string
		assertFalse(person.equals(test));
	}
	
	/**
	 * test that a person object is not equal to null 
	 */
	@Test
	public void testPersonEquals5() {
		Person person = new Person();
		assertFalse(person.equals(null));
	}
	
	/**
	 * test that two person objects are different if their names are different
	 * or if they are assigned different tasks or different hours
	 */
	@Test
	public void testPersonEquals6() {
		Person person1 = new Person();
		Person person2 = new Person();
		Person person3 = new Person();
		
		// prep1
		person1.setName("Mark Smith");
		person2.setName(null);
		
		// test1: names are different; thus persons are different
		assertFalse(person1.equals(person2));
		
		// perp2
		person2.setName("Mark Smith");
		
		Task task = new Task();
		Date testDeadline = Calendar.getInstance().getTime();
		task.setDeadline(testDeadline);
		task.setId(1);
		task.setTitle("Test title");
		task.setDescription("Test description");
		task.setDuration(25);
		task.setDeliverable("Test deliverable");
		task.setPerson("Mark Smith");
		
		person1.addTask(task);
		person2.addTask(null);
		
		// test2: tasks are different; thus persons are different 
		assertFalse(person1.equals(person2));
		
		//prep3
		person3.setName("Mark Smith");
		person3.addTask(task);
		
		// test3 names, tasks, hours are the same; thus persons are equal
		assertTrue(person1.equals(person3));
	}
	
	/**
	 * test setting the name of a person object to an empty string
	 * expect the name to be set to a default value (nobody)
	 */
	@Test
	public void testPersonSetName1() {
		Person person = new Person();
		person.setName("");
		assertEquals("nobody", person.getName());
	}
	
	/**
	 * test setting the name of a person object to a long string
	 * expect the operation to be successful
	 */
	@Test
	public void testPersonSetName2() {
		
		Person person = new Person();
		person.setName(stringBufferLong.toString());
		assertEquals(stringBufferLong.toString(), person.getName());
	}
	
	/**
	 * test setting the name of a person object to null
	 * expect the name to be set to a default value (nobody)
	 */
	@Test
	public void testPersonSetName3() {
		
		Person person = new Person();
		person.setName(null);
		assertEquals("nobody", person.getName());
	}
	
	/**
	 * test setting the name of a person object to string that contains wild characters
	 * expect the operation to be successful
	 */
	@Test
	public void testPersonSetName4() {
		Person person = new Person();
		person.setName(WILD_STRING);
		assertEquals(WILD_STRING, person.getName());
	}
	
	/**
	 * test adding/assigning one task to a person; where the task duration is a positive number 
	 * expect the task has been added successfully 
	 * and expect that the total number hours of the person to be equal to the duration of the task 
	 */
	@Test
	public void testPersonAddTask1() {
	
		String name = "Mark Smith";
		// create a task
		Task task = new Task();
		Date testDeadline = Calendar.getInstance().getTime();
		task.setDeadline(testDeadline);
		task.setId(1);
		task.setTitle("Test title");
		task.setDescription("Test description");
		task.setDuration(25);
		task.setDeliverable("Test deliverable");
		task.setPerson(name);
		
		Person person = new Person();
		person.setName(name);
		person.addTask(task);
		
		assertEquals(person.getHours(), 25 * Person.HOURS_IN_WORKING_DAY);
		
	}
	
	/**
	 * test adding/assigning one task to a person; where the task duration is zero 
	 * expect the task has been added successfully 
	 * However expect that the total number hours of the person to be equal to
	 * a default value (one) as zero is invalid duration
	 */
	@Test
	public void testPersonAddTask2() {
	
		String name = "Mark Smith";
		// create a task with zero duration
		Task task = new Task();
		Date testDeadline = Calendar.getInstance().getTime();
		task.setDeadline(testDeadline);
		task.setId(1);
		task.setTitle("Test title");
		task.setDescription("Test description");
		// zero duration
		task.setDuration(0); 
		task.setDeliverable("Test deliverable");
		task.setPerson(name);
		
		Person person = new Person();
		person.setName(name);
		person.addTask(task);
		
		// expect number of hours to be one
		assertEquals(person.getHours(), 1 * Person.HOURS_IN_WORKING_DAY);
		
	}
	
	/**
	 * test adding/assigning one task to a person; where the task duration is negative 
	 * expect the task has been added successfully 
	 * However expect that the total number hours of the person to be equal to
	 * a default value (one) as negative task duration is invalid
	 */
	@Test
	public void testPersonAddTask3() {
	
		String name = "Mark Smith";
		// create a task with negative duration
		Task task = new Task();
		Date testDeadline = Calendar.getInstance().getTime();
		task.setDeadline(testDeadline);
		task.setId(1);
		task.setTitle("Test title");
		task.setDescription("Test description");
		// negative duration
		task.setDuration(-5);
		task.setDeliverable("Test deliverable");
		task.setPerson(name);
		
		// create a person object and add the task to it
		Person person = new Person();
		person.setName(name);
		person.addTask(task);
		// expect the task to have been added and the hour has been modified to the default value (one)
		assertEquals(person.getHours(), 1 * Person.HOURS_IN_WORKING_DAY);
		
	}
	
	/**
	 * test adding/assigning a task to a person; where the task is null
	 * expect that no task is assigned to person and that the person hours has not changed
	 */
	@Test
	public void testPersonAddTask4() {
		String name = "Mark Smith";
		Person person = new Person();
		person.setName(name);
		// add a null task
		person.addTask(null);
		// expect hours to be zero
		assertEquals(person.getHours(), 0);
	}
	

	/**
	 * test adding/assigning the same task to a person twice; where all task attributes are valid 
	 * expect that the task has been added successfully only once 
	 * and expect that the total number hours of the person to be equal to the duration of one task 
	 */
	@Test
	public void testPersonAddTask5() {
	
		String name = "Mark Smith";
		// create a task
		Task task = new Task();
		Date testDeadline = Calendar.getInstance().getTime();
		task.setDeadline(testDeadline);
		task.setId(1);
		task.setTitle("Test title");
		task.setDescription("Test description");
		task.setDuration(25);
		task.setDeliverable("Test deliverable");
		task.setPerson(name);
		
		Person person = new Person();
		person.setName(name);
		// add the task once
		person.addTask(task);
		// add the task one more time
		person.addTask(task);
		
		// expect the hours of the person to be equal to the duration of only one task
		assertEquals(person.getHours(), 25 * Person.HOURS_IN_WORKING_DAY);
		// expect that the second addition of the task did not happen
		assertEquals(person.getTasks().size(), 1);
	}
	
	/**
	 * test adding/assigning a task to a person; where the task has different person name
	 * expect that the task has been added successfully and its person name has changed to the new assignee name 
	 * and expect that the total number hours of the person to be equal to the duration of the added task 
	 */
	@Test
	public void testPersonAddTask6() {

		// add a task that has different person name assigned
	
		String name1 = "Mark Smith";
		String name2 = "saly kouler";
		// create a task that has person name 1
		Task task = new Task();
		Date testDeadline = Calendar.getInstance().getTime();
		task.setDeadline(testDeadline);
		task.setId(1);
		task.setTitle("Test title");
		task.setDescription("Test description");
		task.setDuration(25);
		task.setDeliverable("Test deliverable");
		task.setPerson(name1);
		
		// create a person with name 2
		Person person = new Person();
		person.setName(name2);
		
		// add the task to the person
		person.addTask(task);
		// expect the task to be added to the person and that his hours has changed
		assertEquals(person.getHours(), 25 * Person.HOURS_IN_WORKING_DAY);
		assertEquals(person.getTasks().size(), 1);

		// expect the person name of the task to have been changed to the new assignee name
		String taskPersonN = person.getTasks().get(0).getPerson(); 
		assertTrue(taskPersonN.equals(person.getName()));
	}
	
	/**
	 * test adding/assigning three different valid tasks to a person
	 * expect that the three tasks have been added successfully 
	 * and expect that the total number hours of the person to be equal to sum of the duration of all three tasks 
	 */
	@Test
	public void testPersonAddTask7() {

		// create threes different tasks
		String name = "Mark Smith";
		Person person = new Person();
		person.setName(name);
		
		Task task1 = new Task();
		Task task2 = new Task();
		Task task3 = new Task();
		
		Date testDeadline = Calendar.getInstance().getTime();
		task1.setDeadline(testDeadline);
		task1.setId(1);
		task1.setTitle("Test title");
		task1.setDescription("Test description");
		task1.setDuration(25);
		task1.setDeliverable("Test deliverable");
		task1.setPerson(name);
		
		task2.setDeadline(testDeadline);
		task2.setId(2);
		task2.setTitle("Test title");
		task2.setDescription("Test description");
		task2.setDuration(25);
		task2.setDeliverable("Test deliverable");
		task2.setPerson(name);
	
		task3.setDeadline(testDeadline);
		task3.setId(3);
		task3.setTitle("Test title");
		task3.setDescription("Test description");
		task3.setDuration(25);
		task3.setDeliverable("Test deliverable");
		task3.setPerson(name);
		
		// add two tasks
		person.addTask(task1);
		person.addTask(task2);
		
		// expect hours and tasklist to be updated
		assertEquals(person.getHours(), 50 * Person.HOURS_IN_WORKING_DAY);
		assertEquals(person.getTasks().size(), 2);

		// add the third task
		person.addTask(task3);
		
		// expect hours and tasklist to be updated
		assertEquals(person.getHours(), 75 * Person.HOURS_IN_WORKING_DAY);
		assertEquals(person.getTasks().size(), 3);
		
	}
	
	/**
	 * test getting the taskList of a newly created person object
	 * expect the tasklist to be empty
	 */
	@Test
	public void testPersonGetTasks1() {
		Person person = new Person();
		assertEquals(person.getTasks().size(), 0);
	}
	
	/**
	 * test getting the taskList of a person that is assigned a task
	 * expect the tasklist to contain one item
	 */
	@Test
	public void testPersonGetTasks2() {
		
		//create a task
		String name = "saly kouler";
		Task task = new Task();
		Date testDeadline = Calendar.getInstance().getTime();
		task.setDeadline(testDeadline);
		task.setId(1);
		task.setTitle("Test title");
		task.setDescription("Test description");
		task.setDuration(25);
		task.setDeliverable("Test deliverable");
		task.setPerson(name);
		
		// create a person and the task to it
		Person person = new Person();
		person.setName(name);
		person.addTask(task);
		
		// test the successful addition of the task
		// and expect the person tasklist to have only one item
		assertEquals(person.getTasks().size(), 1);
		assertEquals(person.getHours(), 25 * Person.HOURS_IN_WORKING_DAY);
		String taskN = person.getTasks().get(0).getPerson(); 
		assertTrue( taskN.equals(person.getName()));
	}
	
	/**
	 * test getting the taskList of a person that is assigned three tasks
	 * expect the tasklist to contain three items
	 */
	@Test
	public void testPersonGetTasks3() {
		
		// create three different tasks
		String name = "Mark Smith";
		Person person = new Person();
		person.setName(name);
		
		Task task1 = new Task();
		Task task2 = new Task();
		Task task3 = new Task();
		
		Date testDeadline = Calendar.getInstance().getTime();
		task1.setDeadline(testDeadline);
		task1.setId(1);
		task1.setTitle("Test title");
		task1.setDescription("Test description");
		task1.setDuration(25);
		task1.setDeliverable("Test deliverable");
		task1.setPerson(name);
		
		task2.setDeadline(testDeadline);
		task2.setId(2);
		task2.setTitle("Test title");
		task2.setDescription("Test description");
		task2.setDuration(25);
		task2.setDeliverable("Test deliverable");
		task2.setPerson(name);
	
		task3.setDeadline(testDeadline);
		task3.setId(3);
		task3.setTitle("Test title");
		task3.setDescription("Test description");
		task3.setDuration(25);
		task3.setDeliverable("Test deliverable");
		task3.setPerson(name);
		
		// add the first two tasks
		person.addTask(task1);
		person.addTask(task2);
		
		// test the successful addition by testing the person hours and tasklist
		assertEquals(person.getHours(), 50 * Person.HOURS_IN_WORKING_DAY);
		assertEquals(person.getTasks().size(), 2);
		
		// add the third task
		person.addTask(task3);
		
		// test the successful addition by testing the person hours and tasklist
		assertEquals(person.getHours(), 75 * Person.HOURS_IN_WORKING_DAY);
		assertEquals(person.getTasks().size(), 3);
	}
	
	/**
	 * test getting the taskList as a string of a newly created person object
	 * expect the tasklist string to be an empty string 
	 */
	@Test
	public void testPersonGetTaskList1() {
		Person person = new Person();
		String tasks = "";
		assertTrue(person.getTasklist().equals(tasks));
	}
	
	/**
	 * test getting the taskList as a string of a person that is assigned a task
	 * expect the tasklist string to be equal to the id to added task 
	 */
	@Test
	public void testPersonGetTaskList2() {
		
		String name = "saly kouler";
		
		Task task = new Task();
		Date testDeadline = Calendar.getInstance().getTime();
		task.setDeadline(testDeadline);
		task.setId(1);
		task.setTitle("Test title");
		task.setDescription("Test description");
		task.setDuration(25);
		task.setDeliverable("Test deliverable");
		task.setPerson(name);
		
		Person person = new Person();
		person.setName(name);
		person.addTask(task);
		
		String tasks = "1";
		assertTrue(person.getTasklist().equals(tasks));
	}
	
	/**
	 * test getting the taskList as string of a person that is assigned three tasks
	 * expect the tasklist string to contain the ids of the three tasks seprated by commas
	 */
	@Test
	public void testPersonGetTaskList3() {

		//create three different tasks
	
		String name = "Mark Smith";
		Person person = new Person();
		person.setName(name);
		
		Task task1 = new Task();
		Task task2 = new Task();
		Task task3 = new Task();
		
		Date testDeadline = Calendar.getInstance().getTime();
		task1.setDeadline(testDeadline);
		task1.setId(1);
		task1.setTitle("Test title");
		task1.setDescription("Test description");
		task1.setDuration(25);
		task1.setDeliverable("Test deliverable");
		task1.setPerson(name);
		
		task2.setDeadline(testDeadline);
		task2.setId(2);
		task2.setTitle("Test title");
		task2.setDescription("Test description");
		task2.setDuration(25);
		task2.setDeliverable("Test deliverable");
		task2.setPerson(name);
	
		task3.setDeadline(testDeadline);
		task3.setId(3);
		task3.setTitle("Test title");
		task3.setDescription("Test description");
		task3.setDuration(25);
		task3.setDeliverable("Test deliverable");
		task3.setPerson(name);
		
		// assign the tasks to the person
		person.addTask(task1);
		person.addTask(task2);
		person.addTask(task3);
		
		// test the tasklist string
		String tasks = "1, 2, 3";
		assertTrue(person.getTasklist().equals(tasks));
	}
}